# uncompyle6 version 3.2.3
# Python bytecode 3.6 (3379)
# Decompiled from: Python 3.6.8 |Anaconda custom (64-bit)| (default, Feb 21 2019, 18:30:04) [MSC v.1916 64 bit (AMD64)]
# Embedded file name: helpers\issue_tracker\clients\base_client.py
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning


class BaseClient:
    """Base client used for every bug tracking platform"""

    timeout = 20

    def __init__(self, target_config, proxies_cb=None, ssl_ca_bundle_path=True):
        """
        Instantiate a minimal viable client handler
        
        Whilst not a strict abstract class, BaseClient is intended to be used only
         by its inherited subclasses rather than being instantiated individually.
        
        Upon initializing the client, the following data is stored as instance variables:
        
          + target_config: dict - Used throughout all clients to parse data
          + username, password: str - Pulled from scan target config for ease of use
          + session: Session - Session object to handle generic request handling
        
        :param target_config: The scan target's config data providing information
                                on how and where to push issues to. A sample format
                                is as follows:
        
                                >>> target_config = {
                                >>>     "bug_tracker": "jira",
                                >>>     "url": "http://localhost:9000/",
                                >>>     "auth": {
                                >>>         "kind": "cookie",
                                >>>        "user": "Acunetix",
                                >>>        "password": "Acunetix"
                                >>>     },
                                >>>     "project": 'Testing',
                                >>>     "issuetype": '10100'  # Mandatory
                                >>> }
        :param proxies_cb: callback function for pulling current requests proxies
        :param ssl_ca_bundle_path: enables/disables ssl certificate verification, useful when using proxies
        """
        self.target_config = target_config
        self.username = self.target_config["auth"]["user"]
        self.password = self.target_config["auth"]["password"]
        self.session = requests.session()
        requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
        self.session.headers.update({"Content-Type": "Application/json"})
        self.proxies_cb = proxies_cb
        self.ssl_ca_bundle_path = ssl_ca_bundle_path
        self._establish_session()

    def _request(self, **kwargs):
        """Basic request handler used by child clients to send requests.
        
        ..note:: Will only send GET or POST requests. It will send a POST request only
                  if a request body is provided, otherwise it will send a GET request.
        
        :param kwargs: Arbitrary keyword arguments defining the properties of the request
        :return: Request object
        """
        endpoint = kwargs.get("endpoint")
        data = kwargs.get("data")
        headers = kwargs.get("headers")
        cookies = kwargs.get("cookies")
        auth = kwargs.get("auth")
        return self.session.request(
            "POST" if data else "GET",
            self.target_config["url"] + endpoint,
            data=data,
            headers=headers,
            cookies=cookies,
            auth=auth,
            verify=False,
            proxies=self.proxies_cb() if self.proxies_cb else None,
            timeout=BaseClient.timeout,
        )

    def _format_vulnerability_description(self, description):
        r"""Format Acunetix vulnerability description to issue-tracker's text formatting style.
        
        :Example:
        
        >>> import helpers.issue_tracker.clients.client as client
        >>>
        >>> description = '<h5>Acunetix</h5>'
        >>> target_config = {
        >>>     "bug_tracker": "jira",
        >>>     "url": "http://localhost:9000/",
        >>>     "auth": {
        >>>         "kind": "cookie",
        >>>         "user": "Acunetix",
        >>>         "password": "Acunetix"
        >>>     },
        >>>     "project": 'Testing'
        >>> }
        >>> client = client.get_client(target_config)
        >>> client._format_vulnerability_description(description)
        >>> '.h5 SQL Injection\n'
        
        :param description: Raw (HTML) vulnerability description provided by Acunetix scanner
        :return: Formatted vulnerability description
        """
        raise NotImplementedError

    def _format_vulnerability_references(self, references):
        """Format Acunetix vulnerability reference pairs to relevant reference links
        
        :Example:
        
        >>> import helpers.issue_tracker.clients.client as client
        >>>
        >>> reference_pair = [['Acunetix SQL Injection Attack', 'http://acunetix.com']]
        >>>
        >>> target_config = {
        >>>     "bug_tracker": "jira",
        >>>     "url": "http://localhost:9000/",
        >>>     "auth": {
        >>>         "kind": "cookie",
        >>>         "user": "Acunetix",
        >>>         "password": "Acunetix"
        >>>     },
        >>>     "project": 'Testing'
        >>> }
        >>>
        >>> jira_client = client.get_client(target_config)
        >>> jira_client._format_vulnerability_references(reference_pair)
        >>> '[Acunetix SQL Injection|http://acunetix.com]'
        >>>
        >>> target_config['bug_tracker'] = 'tfs'
        >>> tfs_client = client.get_client(target_config)
        >>> tfs_client._format_vulnerability_references(reference_pair)
        >>> '<a href="http://acunetix.com">Acunetix SQL Injection</a>'
        
        :param references: List of references returned by Acunetix to parse
        :return: List of reference links (e.g. HTML <a> tags)
        """
        raise NotImplementedError

    def _format_vulnerability_tags(self, tags):
        """Format Acunetix vulnerability tags to prettified tags
        
        :Example:
        
        >>> import helpers.issue_tracker.clients.client as client
        >>>
        >>> tag = 'sql_injection'
        >>> target_config = {
        >>>     "bug_tracker": "jira",
        >>>     "url": "http://localhost:9000/",
        >>>     "auth": {
        >>>         "kind": "cookie",
        >>>         "user": "Acunetix",
        >>>         "password": "Acunetix"
        >>>     },
        >>>     "project": 'Testing'
        >>> }
        >>>
        >>> client = client.get_client(target_config)
        >>> client.__format_vulnerability_tags(tag)
        >>> 'SQL_Injection'
        
        :param tags: List of tags returned by Acunetix to parse
        :return: List of prettified tags
        """
        prettified_tags = []
        pretty_map = {
            "sql_injection": "SQL_Injection",
            "crlf_injection": "CRLF_Injection",
            "http_response_splitting": "HTTP_Response_Splitting",
            "xpath_injection": "XPath_Injection",
            "xss": "XSS",
            "csrf": "CSRF",
            "url_redirection": "URL_Redirection",
            "internal_ip_disclosure": "Internal_IP_Disclosure",
        }
        for tag in tags:
            if tag.startswith("CVE") or tag.startswith("CWE"):
                prettified_tags.append(tag.upper())
            elif tag not in pretty_map:
                prettified_tags.append(tag.title())
            else:
                prettified_tags.append(pretty_map[tag])

        return prettified_tags

    def _establish_session(self):
        """Establishes a new user session with the specific issue tracking platform
        
        :return: True if successful, else False
        """
        raise NotImplementedError

    def add_vulnerability(self, post_data):
        """Create a new issue/bug in the specified bug tracking platform using vulnerability data
        
        :param post_data: Raw vulnerability data pushed by Acunetix scanner
        :return: New Issue ID if successful, else None
        """
        raise NotImplementedError

    def get_projects(self):
        """Pull list of projects available for current authenticated user
        
        :Note:
        
        It is recommended to first make sure that a valid user session
         has been established prior to trying to pull the list of projects.
        
        This method will always return a list, which sometimes may be
         empty ([]). This is either due to the user not having any projects
         or due to the client not being authenticated to the platform.
        
        Making sure that the user is authenticated prior to pulling the
         list of projects makes sure that the list returned reflects
         the actual user's list.
        
        :return: List of available projects
        """
        raise NotImplementedError

    def get_issue_types(self):
        """
        Get list of issue-types for each project available to authenticated user or get
        the list of issue-types for a specific project.
        
        There are common scenarios where multiple projects on the same platform
         may have a different set of issue-types. The method will pull each
         set of issue-types respectively and dump them in JSON format.
        
        However there are cases where you would want the issue-types of a single project
         or repository without having to parse through the entire list (e.g. to ask the
         user to select an issue-type to create issues as)
        
        All issue-types are paired in a product - (ID, NAME)
        
        :Example:
        
        ## No specific repository (repository=None)
        
            {
                'projects': [
                     'ProjectA': {
                        'issuetypes': [('1', 'Bug'), ('2', 'Story')]
                     },
                     'ProjectB': {
                        'issuetypes': [('1', 'Bug')]
                     }
                ]
            }
        
        ## With specified repository (repository='ProjectB')
        
            {
                'ProjectB': {
                        'issuetypes': [('1', 'Bug')]
                }
            }
        
        :return: List-of-Tuples of each issue-type for each project available
        """
        raise NotImplementedError

    def get_issue_status(self, issue_id):
        """Pull status of a specific issue ID (e.g. In Progress, Done)
        
        :Example:
        
        >>> import helpers.issue_tracker.clients.client as client
        >>> target_config = {
        >>>     "bug_tracker": "jira",
        >>>     "url": "http://localhost:9000/",
        >>>     "auth": {
        >>>         "kind": "cookie",
        >>>         "user": "Acunetix",
        >>>         "password": "Acunetix"
        >>>     },
        >>>     "project": 'Testing'
        >>> }
        >>>
        >>> client = client.get_client(target_config)
        >>> client.establish_connection()
        >>> client.get_issue_status(1)
        >>> 'Done'
        >>> client.get_issue_status(-1)
        >>> None
        
        :param issue_id: ID of issue to extract status from
        :return: Status of the specified issue
        """
        raise NotImplementedError
